﻿using Castle.DynamicProxy.Generators;
using PMS.Core.Configuration.CommonEnumConfigs;
using PMS.Core.Utils;
using PMS.Data.DbUtils;
using PMS.Data.Entities.Messages;
using PMS.Data.Entities.Settings;
using PMS.Services.Caching;
using PMS.Services.Caching.Extensions;
using PMS.Services.Utils;
using System;
using System.Collections.Generic;
using System.Linq;

namespace PMS.Services.EntityServices.Messages
{
    /// <summary>
    /// Email account service
    /// </summary>
    public partial class EmailAccountService : IEmailAccountService
    {
        #region Fields

        private readonly EmailAccountSettings _emailAccountSettings;
        private readonly ICacheKeyService _cacheKeyService;
        private readonly IEventDispatcher _eventDispatcher;
        private readonly IRepository<EmailAccount> _emailAccountRepository;

        #endregion

        #region Ctor

        public EmailAccountService(EmailAccountSettings emailAccountSettings,
            ICacheKeyService cacheKeyService,
            IEventDispatcher eventDispatcher,
            IRepository<EmailAccount> emailAccountRepository)
        {
            _emailAccountSettings = emailAccountSettings;
            _cacheKeyService = cacheKeyService;
            _eventDispatcher = eventDispatcher;
            _emailAccountRepository = emailAccountRepository;
        }

        #endregion

        #region Methods

        /// <summary>
        /// Inserts an email account
        /// </summary>
        /// <param name="emailAccount">Email account</param>
        public virtual void InsertEmailAccount(EmailAccount emailAccount)
        {
            if (emailAccount == null)
                throw new ArgumentNullException(nameof(emailAccount));

            emailAccount.Email = CommonHelper.EnsureNotNull(emailAccount.Email);
            emailAccount.DisplayName = CommonHelper.EnsureNotNull(emailAccount.DisplayName);
            emailAccount.Host = CommonHelper.EnsureNotNull(emailAccount.Host);
            emailAccount.Username = CommonHelper.EnsureNotNull(emailAccount.Username);
            emailAccount.Password = CommonHelper.EnsureNotNull(emailAccount.Password);

            emailAccount.Email = emailAccount.Email.Trim();
            emailAccount.ReplyTo = string.IsNullOrEmpty(emailAccount.ReplyTo)
                                 ? ""
                                 : emailAccount.ReplyTo.Trim();
            emailAccount.DisplayName = emailAccount.DisplayName.Trim();
            emailAccount.Host = emailAccount.Host.Trim();
            emailAccount.Username = emailAccount.Username.Trim();
            emailAccount.Password = emailAccount.Password.Trim();
            
            emailAccount.Email = CommonHelper.EnsureMaximumLength(emailAccount.Email, 200);
            emailAccount.ReplyTo = CommonHelper.EnsureMaximumLength(emailAccount.Email, 200);

            if ((!CommonHelper.IsValidEmail(emailAccount.Email) && !string.IsNullOrEmpty(emailAccount.Email))
             || (!CommonHelper.IsValidEmail(emailAccount.ReplyTo) && !string.IsNullOrEmpty(emailAccount.ReplyTo)))
                throw new PMSException("Email Account is not valid", Core.Configuration.CommonEnumConfigs.SystemLogLevel.Error);

            emailAccount.DisplayName = CommonHelper.EnsureMaximumLength(emailAccount.DisplayName, 200);
            emailAccount.Host = CommonHelper.EnsureMaximumLength(emailAccount.Host, 128);
            emailAccount.Username = CommonHelper.EnsureMaximumLength(emailAccount.Username, 200);
            emailAccount.Password = CommonHelper.EnsureMaximumLength(emailAccount.Password, 200);

            _emailAccountRepository.Insert(emailAccount);

            //event notification
            _eventDispatcher.EntityInserted(emailAccount);
        }

        /// <summary>
        /// Updates an email account
        /// </summary>
        /// <param name="emailAccount">Email account</param>
        public virtual void UpdateEmailAccount(EmailAccount emailAccount)
        {
            if (emailAccount == null)
                throw new ArgumentNullException(nameof(emailAccount));

            emailAccount.Email = CommonHelper.EnsureNotNull(emailAccount.Email);
            emailAccount.DisplayName = CommonHelper.EnsureNotNull(emailAccount.DisplayName);
            emailAccount.Host = CommonHelper.EnsureNotNull(emailAccount.Host);
            emailAccount.Username = CommonHelper.EnsureNotNull(emailAccount.Username);
            emailAccount.Password = CommonHelper.EnsureNotNull(emailAccount.Password);

            emailAccount.Email = emailAccount.Email.Trim();
            emailAccount.ReplyTo = string.IsNullOrEmpty(emailAccount.ReplyTo)
                                 ? ""
                                 : emailAccount.ReplyTo.Trim();
            emailAccount.DisplayName = emailAccount.DisplayName.Trim();
            emailAccount.Host = emailAccount.Host.Trim();
            emailAccount.Username = emailAccount.Username.Trim();
            emailAccount.Password = emailAccount.Password.Trim();

            emailAccount.Email = CommonHelper.EnsureMaximumLength(emailAccount.Email, 200);
            emailAccount.ReplyTo = CommonHelper.EnsureMaximumLength(emailAccount.Email, 200);

            if ((!CommonHelper.IsValidEmail(emailAccount.Email) && !string.IsNullOrEmpty(emailAccount.Email))
             || (!CommonHelper.IsValidEmail(emailAccount.ReplyTo) && !string.IsNullOrEmpty(emailAccount.ReplyTo)))
                throw new PMSException("Email Account is not valid", Core.Configuration.CommonEnumConfigs.SystemLogLevel.Error);

            emailAccount.DisplayName = CommonHelper.EnsureMaximumLength(emailAccount.DisplayName, 200);
            emailAccount.Host = CommonHelper.EnsureMaximumLength(emailAccount.Host, 128);
            emailAccount.Username = CommonHelper.EnsureMaximumLength(emailAccount.Username, 200);
            emailAccount.Password = CommonHelper.EnsureMaximumLength(emailAccount.Password, 200);

            _emailAccountRepository.Update(emailAccount);

            //event notification
            _eventDispatcher.EntityUpdated(emailAccount);
        }

        /// <summary>
        /// Deletes an email account
        /// </summary>
        /// <param name="emailAccount">Email account</param>
        public virtual void DeleteEmailAccount(EmailAccount emailAccount)
        {
            if (emailAccount == null)
                throw new ArgumentNullException(nameof(emailAccount));

            if (GetAllEmailAccounts().Count == 1 && _emailAccountSettings.Enabled)
                throw new PMSException("You cannot delete this email account. At least one account is required when email Service Enabled.");

            _emailAccountRepository.Delete(emailAccount);

            //event notification
            _eventDispatcher.EntityDeleted(emailAccount);
        }

        /// <summary>
        /// Gets an email account by identifier
        /// </summary>
        /// <param name="emailAccountId">The email account identifier</param>
        /// <returns>Email account</returns>
        public virtual EmailAccount GetEmailAccountById(int emailAccountId)
        {
            if (emailAccountId == 0)
                return null;

            return _emailAccountRepository.ToCachedGetById(emailAccountId);
        }

        /// <summary>
        /// Gets an email account by Email
        /// </summary>
        /// <param name="email"></param>
        /// <param name="accountType"></param>
        /// <returns></returns>
        public virtual EmailAccount GetEmailAccountByEmail(string email, EmailAccountType accountType = EmailAccountType.AliyunDirectMail)
        {
            if (string.IsNullOrEmpty(email) || !CommonHelper.IsValidEmail(email))
                return null;

            var accounts = GetAllEmailAccounts();
            return accounts.FirstOrDefault(account => account.Email.Equals(email, StringComparison.InvariantCultureIgnoreCase) && account.EmailAccountType == accountType);
        }

        /// <summary>
        /// Gets all email accounts
        /// </summary>
        /// <returns>Email accounts list</returns>
        public virtual IList<EmailAccount> GetAllEmailAccounts()
        {
            var query = from ea in _emailAccountRepository.Table
                        orderby ea.Id
                        select ea;

            var emailAccounts = query.ToCachedList(_cacheKeyService.PrepareKeyForDefaultCache(CacheKeyConfigs.EmailAccountsAllCacheKey));

            return emailAccounts;
        }

        #endregion
    }
}
